package name.weily.liba.widgets

import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Build
import android.provider.Settings
import android.util.Log
import android.view.*
import android.widget.ImageView
import name.weily.liba.tools.TransparentPermissionActivity
import kotlin.math.abs

/**
 * FloatMenu: display standalone menu on desktop by using WindowManager.addView
 */
class FloatWidget(view: View) {
    companion object {
        private const val systemOverlayPermission = android.Manifest.permission.SYSTEM_ALERT_WINDOW
        private const val dragging_min_size = 3.5f

        const val DIRECTION_NONE = -1
        const val DIRECTION_ANY = 0
        const val DIRECTION_HORIZONTAL = 1
        const val DIRECTION_VERTICAL = 2

        private val Tag by lazy { FloatWidget::class.java.simpleName }
    }

    private var view: View? = null

    private val lps by lazy {
        WindowManager.LayoutParams().apply {
            type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
            } else {
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
            }
            flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
            format = PixelFormat.TRANSLUCENT
            width = WindowManager.LayoutParams.WRAP_CONTENT
            height = WindowManager.LayoutParams.WRAP_CONTENT
            gravity = Gravity.CENTER
        }
    }
    private var lastX = 0f
    private var lastY = 0f
    private var isDragging = false
    private var movedX = 0f
    private var movedY = 0f

    /**
    * one of [FloatWidget.DIRECTION_NONE],
    * [FloatWidget.DIRECTION_ANY], [FloatWidget.DIRECTION_HORIZONTAL], [FloatWidget.DIRECTION_VERTICAL],
    * default value is [FloatWidget.DIRECTION_ANY]
    */
    var allowDirection = DIRECTION_ANY

    init {
        view.also { v ->
            this.view = v
            //to avoid shaking of view use rawX/Y instead x/y
            v.setOnTouchListener { v1, event ->

                if (allowDirection == DIRECTION_NONE) {
                    return@setOnTouchListener false
                }

                //every event's rawX/Y
                val rawX = event.rawX
                val rawY = event.rawY

                when (event.action) {
                    MotionEvent.ACTION_DOWN -> {
                        lastX = rawX
                        lastY = rawY
                        isDragging = false
                        movedX = 0f
                        movedY = 0f
                    }
                    MotionEvent.ACTION_MOVE -> {
                        val deltaX = rawX - lastX
                        val deltaY = rawY - lastY
                        when (allowDirection) {
                            DIRECTION_ANY -> {
                                lps.x += deltaX.toInt()
                                lps.y += deltaY.toInt()
                            }
                            DIRECTION_HORIZONTAL -> lps.x += deltaX.toInt()
                            DIRECTION_VERTICAL -> lps.y += deltaY.toInt()
                        }

                        getWindowManager()?.updateViewLayout(this.view, lps)

                        // need update lastX/Y every move step, their value are't constant
                        lastX = rawX
                        lastY = rawY

                        //check drag distance to judge is dragging or not
                        if (!isDragging) {
                            movedX += deltaX
                            movedY += deltaY
                            if (abs(movedX) >= dragging_min_size || abs(movedY) >= dragging_min_size) {
                                isDragging = true
                            }
                        }
                        Log.d("ff", "Ffff $movedX   $movedY")
                    }
                    MotionEvent.ACTION_UP -> {
                        return@setOnTouchListener isDragging
                    }
                    MotionEvent.ACTION_CANCEL -> {
                        return@setOnTouchListener isDragging
                    }
                }
                return@setOnTouchListener false
            }
        }
    }

    constructor(context: Context, resId: Int) : this(LayoutInflater.from(context).inflate(resId, null)) {}

    private fun getWindowManager(): WindowManager? {
        return view?.context?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager
    }

    private fun havePermission(): Boolean {
        return view?.let {
            return@let if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                Settings.canDrawOverlays(it.context)
            } else {
                return true
            }
        } ?: false
    }

    inline fun <reified T : View> findViewById(rid: Int): T? {
        return getRootView()?.findViewById<T>(rid)
    }

    fun setOnClickListener(listener: ((View) -> Unit)) {
        this.view?.setOnClickListener(listener)
    }

    fun setOnClickListener(listener: View.OnClickListener) {
        this.view?.setOnClickListener(listener)
    }

    fun getRootView(): View? = view

    fun attachToWindow() {
        if (!havePermission()) {
            val intent = Intent(view?.context, TransparentPermissionActivity::class.java)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            intent.putExtra("permissions", arrayOf(systemOverlayPermission))
            view?.context?.startActivity(intent)
            return
        }

        if (!view!!.isAttachedToWindow) {
            getWindowManager()?.addView(view, lps)
        }
    }

    fun detachFromWindow() {
        view?.apply {
            if (isAttachedToWindow) {
                getWindowManager()?.removeViewImmediate(this)
            }
        }
    }

    fun destroy() {
        detachFromWindow()
        view = null
    }
}

fun ImageView.postSetImageResource(resId: Int) {
    post {
        setImageResource(resId)
    }
}